package com.hex.ds.hdrs.engine.manager;

import com.hex.ds.hdrs.common.constant.Const;
import com.hex.ds.hdrs.common.constant.ErrorCode;
import com.hex.ds.hdrs.common.exception.BizException;
import com.hex.ds.hdrs.period.job.po.JobTask;
import com.hex.ds.hdrs.period.task.po.PeriodTask;
import com.hex.ds.hdrs.period.task.util.TaskTrackHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Package: com.hex.ds.hdrs.engine.manager
 * @ClassName RunningManager
 * @Description: 正在运行队列管理
 * @Author: jd.chen
 * @Date 2021/7/5 17:55
 * @Version v1.0
 **/
@Slf4j
@Component
public class RunningManager implements ApplicationContextAware {

    private static ApplicationContext context;

    /**
     * runJobList 同步锁
     */
    private final byte[] jobListSyn = {};
    /**
     * runJobMap 同步锁
     */
    private final byte[] jobMapSyn = {};
    /**
     * runTaskList 同步锁
     */
    private final byte[] taskListSyn = {};
    /**
     * runTaskMap 同步锁
     */
    private final byte[] taskMapSyn = {};

    /**
     * 正在执行任务队列
     */
    private List<JobTask> runJobList = Collections.synchronizedList(new ArrayList<>());
    /**
     * 正在执行任务map<jobCode,jobtask>
     */
    private Map<String, JobTask> runJobMap = new ConcurrentHashMap<>();
    /**
     * 正在执行任务队列
     */
    private List<PeriodTask> runTaskList = Collections.synchronizedList(new ArrayList<>());
    /**
     * 正在执行任务map<taskCode,PeriodTask>
     */
    private Map<String, PeriodTask> runTaskMap = new ConcurrentHashMap<>();

    private List<PeriodTask> tempTaskList = Collections.synchronizedList(new ArrayList<>());


    /**
     * @Method: init
     * @Param: [runJobList, runJobMap, runTaskList, runTaskMap]
     * @Return: void
     * @Description: running初始化
     * @Author: ji.tang
     * @Date: 2022/12/21 15:38
     * @Version: V1.0
     */
    public void init(List<JobTask> runJobList, Map<String, JobTask> runJobMap, List<PeriodTask> runTaskList, Map<String, PeriodTask> runTaskMap) {
        clear();
        log.info("running初始化【RunningManager.init】======》");
        this.runJobList.addAll(runJobList);
        this.runJobMap.putAll(runJobMap);
        this.runTaskList.addAll(runTaskList);
        this.runTaskMap.putAll(runTaskMap);
    }


    /**
     * @Method: clear
     * @Param: []
     * @Return: void
     * @Description: running清空内存
     * @Author: ji.tang
     * @Date: 2022/12/21 15:38
     * @Version: V1.0
     */
    public void clear() {
        synchronized (this){
            runJobList.clear();
            runJobMap.clear();
            runTaskList.clear();
            runTaskMap.clear();
        }
    }

    /**
     * @Method: clearOne
     * @Param: [job, task]
     * @Return: void
     * @Description: running中删除指定的作业任务（删除一个）
     * @Author: ji.tang
     * @Date: 2022/12/21 15:39
     * @Version: V1.0
     */
    public void clearOne(JobTask job, PeriodTask task){
        String jobCode = job.getJobCode();
        String taskCode = task.getTaskCode();
        synchronized (this){
            runJobList.remove(job);
            runJobMap.remove(jobCode);
            runTaskList.remove(task);
            runTaskMap.remove(taskCode);
        }
    }

    /**
     * @Method: removeJob
     * @Param: [task, type] type:类型，描述从那个位置删除
     * @Return: void
     * @Description:  running删除指定的作业任务
     * @Author: ji.tang
     * @Date: 2022/12/21 15:42
     * @Version: V1.0
     */
    public void removeJob(PeriodTask task, String type) {
        try {
            String jobCode = task.getJobCode();
            log.debug("类型【{}】=====》【running removeJob】《=====作业【{}】、任务【{}】、日期【{}】", type, jobCode, task.getTaskCode(), task.getBizDate());
            JobTask jobTask = this.getJob(jobCode);
            synchronized (taskListSyn) {
                this.runTaskList.remove(task);
                this.runTaskMap.remove(task.getTaskCode());
            }
            synchronized (jobListSyn) {
                this.runJobList.remove(jobTask);
                this.runJobMap.remove(jobCode);
            }
        } catch (Exception e) {
            log.error("类型【{}】【running removeJob】异常:{}", type, e.getMessage());
        }
    }

    /**
     * @Method: removeNullTask
     * @Param: [jobCode, taskCode]
     * @Return: void
     * @Description: 移除取到为null的任务
     * @Author: ji.tang
     * @Date: 2022/12/21 15:44
     * @Version: V1.0
     */
    public void removeNullTask(String jobCode, String taskCode) {
        try {
            synchronized (taskListSyn) {
                Iterator<PeriodTask> taskIter = runTaskList.iterator();
                while (taskIter.hasNext()) {
                    PeriodTask task = taskIter.next();
                    if (task != null && taskCode.equals(task.getTaskCode())) {
                        taskIter.remove();
                    }
                }
                runTaskMap.remove(taskCode);
            }
            synchronized (jobListSyn) {
                Iterator<JobTask> jobIter = runJobList.iterator();
                while (jobIter.hasNext()) {
                    JobTask job = jobIter.next();
                    if (job != null && jobCode.equals(job.getJobCode())) {
                        jobIter.remove();
                    }
                }
                runJobMap.remove(jobCode);
            }
        } catch (Exception e) {
            log.error("任务【{}】【running removeNullTask】异常:{}", taskCode, e.getMessage());
        }
    }

    /**
     * @Method: checkDelJobTask
     * @Param: [job, task, type]
     * @Return: void
     * @Description: 判断作业任务和内存中的是否一致
     * @Author: ji.tang
     * @Date: 2022/12/21 15:45
     * @Version: V1.0
     */
    public void checkDelJobTask(JobTask job, PeriodTask task, String type) {
        try {
            String jobCode = job.getJobCode();
            String bizDate = job.getBizDate();
            String taskCode = task.getTaskCode();
            String taskBizDate = task.getBizDate();
            if (runTaskList.contains(task)) {
                log.error("类型【{}】任务【{}】、日期【{}】【checkDelJobTask】runTaskList删除失败", type, taskCode, taskBizDate);
            }
            if (this.isContainsTask(taskCode)) {
                log.error("类型【{}】任务【{}】、日期【{}】【checkDelJobTask】runTaskMap删除失败", type, taskCode, taskBizDate);
            }
            if (runJobList.contains(job)) {
                log.error("类型【{}】作业【{}】、日期【{}】【checkDelJobTask】runJobList删除失败", type, jobCode, bizDate);
            }
            if (this.isContainsJob(jobCode)) {
                log.error("类型【{}】作业【{}】、日期【{}】【checkDelJobTask】runJobMap删除失败", type, jobCode, bizDate);
            }
        } catch (Exception e) {
            log.error("【checkDelJobTask】异常:{}", e.getMessage());
        }
    }

    /**
     * @Method: addJob
     * @Param: [job, task, type]
     * @Return: void
     * @Description: 添加作业
     * @Author: ji.tang
     * @Date: 2022/12/21 15:48
     * @Version: V1.0
     */
    public void addJob(JobTask job, PeriodTask task, String type) {
        try {
            log.debug("=====》【running addJob】《=====作业【{}】、任务【{}】、日期【{}】", task.getJobCode(), task.getTaskCode(), task.getBizDate());
            TaskTrackHelper.addTaskTrack(task, TaskTrackHelper.dispatcherSucc(type));
            synchronized (taskListSyn) {
                if (!addRunTaskList(task)) {
                    log.error("类型【{}】running新增任务失败，作业【{}】、任务【{}】、日期【{}】", type, job.getJobCode(), task.getTaskCode(), task.getBizDate());
                } else {
                    this.runTaskMap.put(task.getTaskCode(), task);
                }
            }
            synchronized (jobListSyn) {
                if (!addRunJobList(job)) {
                    log.error("类型【{}】running新增作业失败，作业【{}】、任务【{}】、日期【{}】", type, job.getJobCode(), task.getTaskCode(), task.getBizDate());
                }else {
                    this.runJobMap.put(task.getJobCode(), job);
                }
            }
        } catch (Exception e) {
            log.error("【running addJob】异常:{}", e.getMessage(), e);
        }
        //校验数据是否正确添加
        if (!checkAddJobTask(job,task)){
            log.error("类型【{}】running新增作业失败，作业【{}】、任务【{}】、日期【{}】", type, job.getJobCode(), task.getTaskCode(), task.getBizDate());
            //内存数据添加不一致，删除
            this.clearOne(job, task);
            throw new BizException(ErrorCode.ERROR_820011);
        }
    }

    /**
     * @Method: checkAddJobTask
     * @Param: [job, task]
     * @Return: boolean
     * @Description: 判断作业任务在内存中是否存在
     * @Author: ji.tang
     * @Date: 2022/12/21 15:48
     * @Version: V1.0
     */
    public boolean checkAddJobTask(JobTask job, PeriodTask task) {
        return runTaskList.contains(task)
                && this.isContainsTask(task.getTaskCode())
                && runJobList.contains(job)
                && this.isContainsJob(job.getJobCode());
    }

    /**
     * @Method: addRunTaskList
     * @Param: [task]
     * @Return: boolean
     * @Description: runTaskList添加任务
     * @Author: ji.tang
     * @Date: 2022/12/21 15:49
     * @Version: V1.0
     */
    public boolean addRunTaskList(PeriodTask task){
        boolean flag = false;
        if (!runTaskList.contains(task)) {
            flag = runTaskList.add(task);
        }
        return flag;
    }

    /**
     * @Method: addRunJobList
     * @Param: [job]
     * @Return: boolean
     * @Description: runJobList中添加作业任务
     * @Author: ji.tang
     * @Date: 2022/12/21 15:49
     * @Version: V1.0
     */
    public boolean addRunJobList(JobTask job){
        boolean flag = false;
        if (!runJobList.contains(job)) {
            flag = runJobList.add(job);
        }
        return flag;
    }

    /**
     * 处理任务异常
     *
     * @param task
     * @param error
     */
    public void taskDealFail(PeriodTask task, String error) {
        TaskTrackHelper.addTaskTrack(task, TaskTrackHelper.dispatcherFail(error));
    }


    public boolean isContainsJob(String jobCode) {
        return this.getJob(jobCode) == null ? false : true;
    }

    public boolean isContainsTask(String taskCode) {
        return this.getTask(taskCode) == null ? false : true;
    }

    /**
     * 获取作业
     *
     * @param jobCode
     * @return
     */
    public JobTask getJob(String jobCode) {
        synchronized (jobMapSyn) {
            return runJobMap.get(jobCode);
        }
    }

    /**
     * 获取任务
     *
     * @param taskCode
     * @return
     */

    public PeriodTask getTask(String taskCode) {
        synchronized (taskMapSyn) {
            return runTaskMap.get(taskCode);
        }
    }

    /**
     * 正在执行队列  添加到 待处理队列
     *
     * @param jobTask
     * @param curTask
     * @param toDoManager
     */
    public void runningToToDo(JobTask jobTask, PeriodTask curTask, ToDoManager toDoManager) {
        log.info("=====》【running runningToToDo】《=====作业【{}】、任务【{}】、日期【{}】", curTask.getJobCode(), curTask.getTaskCode(), curTask.getBizDate());
        toDoManager.addJob(jobTask, Const.TRACK_TYPE_RUNNING_TO_DO);
        this.removeJob(curTask, "RUNNING_TO_DO");
    }


    /**
     * 正在执行队列  添加到 可执行队列
     *
     * @param jobTask
     * @param downTask
     * @param curTask
     * @param toRunManager
     */
    public void touchNextTaskToToRun(JobTask jobTask, PeriodTask downTask, PeriodTask curTask, ToRunManager toRunManager) {
        log.info("=====》【running touchNextTaskToToRun】《=====作业【{}】、任务【{}】、日期【{}】", downTask.getJobCode(), downTask.getTaskCode(), downTask.getBizDate());
        this.removeJob(curTask, "TOUCH_TO_RUN");
        toRunManager.addJob(jobTask, downTask, Const.TRACK_TYPE_TOUCH_TO_RUN);
    }

    public List<JobTask> getRunJobList() {
        return runJobList;
    }

    public Map<String, JobTask> getRunJobMap() {
        return runJobMap;
    }

    public List<PeriodTask> getRunTaskList() {
        return runTaskList;
    }

    public Map<String, PeriodTask> getRunTaskMap() {
        return runTaskMap;
    }

    public List<PeriodTask> getTempTaskList() {
        return tempTaskList;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        context = applicationContext;
    }

    public static RunningManager getInstance() {
        return context.getBean(RunningManager.class);
    }
}
